/*
 * @Author: xiaosihan
 * @Date: 2023-08-25 21:54:17
 * @Last Modified by: xiehangyun
 * @Last Modified time: 2025-02-22 17:02:30
 */

import { fabric } from 'fabric';
import { IImageOptions } from 'fabric/fabric-impl';
import { Color } from 'three';

// 画布遮罩
export default class FabricMark extends fabric.Image {
  constructor(
    element: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement,
    options?: Partial<IImageOptions>,
  ) {
    super(element, options);
  }

  markColor = '#000000';

  originX = 'center';

  originY = 'center';

  left = 2048;

  top = 2048;

  width = 4096;

  height = 4096;

  //uv轮廓图的边厂单位mm
  sideLength = 500;

  globalCompositeOperation = 'destination-over';

  // 通过svg设置遮罩
  async setvPolygonBySvg(svgUrl: string) {
    if (!svgUrl) {
      return;
    }

    await new Promise<void>((resolve, reject) => {
      fetch(svgUrl)
        .then((response) => response.text())
        .then((svgText) => {
          const color = new Color();
          const colors = svgText.match(/#[a-fA-F\d]{6}/g) || [];
          for (let c of colors) {
            color.set(c);
            if (color.r > color.g + 0.5 && color.r > color.b + 0.5) {
              svgText = svgText.replaceAll(c, `${c};display: none;`);
            } else if (color.r < 0.1 && color.g < 0.1 && color.b < 0.1) {
              svgText = svgText.replaceAll(c, `${c};fill:${this.markColor};`);
            }
          }

          //路径转多边形
          // 正则表达式模式，用于匹配路径数据
          const pathRegex = / d="([^"]+)"/;

          // 提取路径数据
          const match = svgText.match(pathRegex) || [];
          if (match.length) {
            let pathData = match[1] || '';
            const Mpath = pathData.split('M');
            svgText = svgText.replace(pathData, `M${Mpath[1] || ''}`);
          }
          // 创建 Blob 对象
          const blob = new Blob([svgText], { type: 'image/svg+xml' });
          // 创建临时 URL 并设置给 img.src
          const url = URL.createObjectURL(blob);

          this.setSrc(
            url,
            () => {
              const { width, height } = this.getElement();
              const maxSide = Math.max(width, height);
              const scale = 4096 / maxSide;
              (this as fabric.Image).set({
                originX: 'left',
                originY: 'top',
                left: (4096 - width * scale) / 2,
                top: (4096 - height * scale) / 2,
                scaleX: scale,
                scaleY: scale,
              });
              this.dirty = false;
              resolve();
            },
            { crossOrigin: 'anonymous' },
          );
        });
    });
  }

  objectCaching = false;

  // 矩形遮罩
  mark_x = 0;
  mark_y = 0;
  mark_width = 4096;
  mark_height = 4096;

  // 裁剪矩形遮罩
  clip_mark_x = 0;
  clip_mark_y = 0;
  clip_mark_width = 4096;
  clip_mark_height = 4096;

  // 缓存(导出生产文件用的)
  cacheCanvas = (() => {
    const cacheCanvas = document.createElement('canvas');
    cacheCanvas.width = 4096;
    cacheCanvas.height = 4096;
    return cacheCanvas;
  })();

  //在页面上用的
  miniCacheCanvas = (() => {
    const miniCacheCanvas = document.createElement('canvas');
    miniCacheCanvas.width = 1024;
    miniCacheCanvas.height = 1024;
    return miniCacheCanvas;
  })();

  // 设置矩形遮罩
  setRectMark(x: number, y: number, width: number, height: number) {
    this.mark_x = x * 4096;
    this.mark_y = y * 4096;
    this.mark_width = width * 4096;
    this.mark_height = height * 4096;
    this.dirty = false;
  }

  // 设置矩形的遮罩的裁剪
  setClipRectMark(x: number, y: number, width: number, height: number) {
    this.clip_mark_x = x * 4096;
    this.clip_mark_y = y * 4096;
    this.clip_mark_width = width * 4096;
    this.clip_mark_height = height * 4096;
    this.dirty = false;
  }

  //过滤目标canvas
  filter(
    ctx: CanvasRenderingContext2D,
    sideLength: number = 4096,
    ratio: number = window.devicePixelRatio,
  ): void {
    const cacheCtx = this.cacheCanvas.getContext('2d')!;
    cacheCtx.save();
    cacheCtx.globalCompositeOperation = 'source-over';
    cacheCtx.resetTransform();
    cacheCtx.fillStyle = '#ffffff';
    cacheCtx.globalAlpha = 1;
    cacheCtx.clearRect(0, 0, 4096, 4096);
    cacheCtx.fillRect(0, 0, 4096, 4096);
    cacheCtx.restore();

    cacheCtx.setTransform(this.scaleX!, 0, 0, this.scaleY!, 2048, 2048);
    cacheCtx.globalCompositeOperation = 'xor';
    super.drawObject(cacheCtx);

    cacheCtx.save();
    cacheCtx.globalCompositeOperation = 'source-over';
    cacheCtx.resetTransform();
    cacheCtx.fillStyle = '#ffffff';
    cacheCtx.globalAlpha = 1;
    cacheCtx.fillRect(0, 0, 4096, this.mark_y / this.sideLength);
    cacheCtx.fillRect(0, 0, this.mark_x / this.sideLength, 4096);
    cacheCtx.fillRect(
      (this.mark_x + this.mark_width) / this.sideLength,
      0,
      4096 - (this.mark_x - this.mark_width) / this.sideLength,
      4096,
    );
    cacheCtx.fillRect(
      0,
      (this.mark_y + this.mark_height) / this.sideLength,
      4096,
      4096 - (this.mark_y - this.mark_height) / this.sideLength,
    );
    cacheCtx.fillRect(
      this.clip_mark_x / this.sideLength,
      this.clip_mark_y / this.sideLength,
      this.clip_mark_width / this.sideLength,
      this.clip_mark_height / this.sideLength,
    );
    cacheCtx.restore();

    ctx.save();
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.globalCompositeOperation = 'destination-out';
    ctx.globalAlpha = 1;
    ctx.drawImage(this.cacheCanvas, 0, 0, 4096, 4096, 0, 0, sideLength * ratio, sideLength * ratio);
    ctx.restore();
  }

  // 裁剪目标canvas, 去掉轮廓和绘制区域的像素点
  clip(
    ctx: CanvasRenderingContext2D,
    sideLength: number = 4096,
    ratio: number = window.devicePixelRatio,
  ): void {
    const cacheCtx = this.cacheCanvas.getContext('2d')!;
    cacheCtx.save();
    cacheCtx.globalCompositeOperation = 'source-over';
    cacheCtx.resetTransform();
    // cacheCtx.fillStyle = "#ffffff";
    // cacheCtx.globalAlpha = 1;
    // cacheCtx.clearRect(0, 0, 4096, 4096);
    // cacheCtx.fillRect(0, 0, 4096, 4096);
    cacheCtx.restore();

    cacheCtx.setTransform(this.scaleX!, 0, 0, this.scaleY!, 2048, 2048);
    cacheCtx.globalCompositeOperation = 'xor';
    // super.drawObject(cacheCtx);

    cacheCtx.save();
    cacheCtx.globalCompositeOperation = 'source-over';
    cacheCtx.resetTransform();
    cacheCtx.fillStyle = '#ff00ff';
    cacheCtx.globalAlpha = 1;
    cacheCtx.fillRect(0, 0, 4096, this.mark_y / this.sideLength);
    cacheCtx.fillRect(0, 0, this.mark_x / this.sideLength, 4096);
    cacheCtx.fillRect(
      (this.mark_x + this.mark_width) / this.sideLength,
      0,
      4096 - (this.mark_x - this.mark_width) / this.sideLength,
      4096,
    );
    cacheCtx.fillRect(
      0,
      (this.mark_y + this.mark_height) / this.sideLength,
      4096,
      4096 - (this.mark_y - this.mark_height) / this.sideLength,
    );
    cacheCtx.fillRect(
      this.clip_mark_x / this.sideLength,
      this.clip_mark_y / this.sideLength,
      this.clip_mark_width / this.sideLength,
      this.clip_mark_height / this.sideLength,
    );
    cacheCtx.restore();

    ctx.save();
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.globalCompositeOperation = 'destination-out';
    ctx.globalAlpha = 1;
    ctx.drawImage(this.cacheCanvas, 0, 0, 4096, 4096, 0, 0, sideLength * ratio, sideLength * ratio);
    ctx.restore();
  }

  // 只裁剪轮廓,不包含局部绘制区域
  clipOutLine(
    ctx: CanvasRenderingContext2D,
    sideLength: number = 1024,
    ratio: number = window.devicePixelRatio,
    color: string = '#f5f5f5',
    opacity: number = 1,
  ): void {
    const miniCacheCtx = this.miniCacheCanvas.getContext('2d')!;
    miniCacheCtx.save();
    miniCacheCtx.globalCompositeOperation = 'source-over';
    miniCacheCtx.resetTransform();
    miniCacheCtx.fillStyle = color; // "#f5f5f5";
    miniCacheCtx.globalAlpha = 1;
    miniCacheCtx.clearRect(0, 0, 1024, 1024);
    miniCacheCtx.fillRect(0, 0, 1024, 1024);
    // miniCacheCtx.clearRect(this.clip_mark_x * 1024, this.clip_mark_y * 1024, this.clip_mark_width * 1024, this.clip_mark_height * 1024);
    miniCacheCtx.restore();

    miniCacheCtx.setTransform(this.scaleX! * 0.25, 0, 0, this.scaleY! * 0.25, 512, 512);
    miniCacheCtx.globalCompositeOperation = 'xor';
    super.drawObject(miniCacheCtx);

    ctx.save();
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.globalAlpha = opacity;
    ctx.drawImage(this.miniCacheCanvas, 0, 0, sideLength * ratio, sideLength * ratio);
    ctx.restore();
  }

  // 绘制一个黑色的遮罩层,包括轮廓 和 绘制区域
  drawObject(ctx: CanvasRenderingContext2D): void {
    const cacheCtx = this.cacheCanvas.getContext('2d')!;
    cacheCtx.save();
    cacheCtx.globalCompositeOperation = 'source-over';
    cacheCtx.resetTransform();
    cacheCtx.fillStyle = '#f5f5f5';
    cacheCtx.globalAlpha = 0.9;
    cacheCtx.clearRect(0, 0, 4096, 4096);
    cacheCtx.fillRect(0, 0, 4096, 4096);
    cacheCtx.restore();
    cacheCtx.setTransform(this.scaleX!, 0, 0, this.scaleY!, 2048, 2048);
    cacheCtx.globalCompositeOperation = 'xor';
    if (this.mark_x > 0 && this.mark_y > 0) {
      cacheCtx.resetTransform();
      cacheCtx.clearRect(
        this.mark_x / this.sideLength,
        this.mark_y / this.sideLength,
        this.mark_width / this.sideLength,
        this.mark_height / this.sideLength,
      );
    } else {
      super.drawObject(cacheCtx);
    }
    //如果是局部打印的话 就绘制一个红白相间的打印框
    if (this.mark_x > 0 && this.mark_y > 0) {
      cacheCtx.save();
      cacheCtx.globalCompositeOperation = 'source-over';
      cacheCtx.resetTransform();
      cacheCtx.strokeStyle = '#cccccc';
      cacheCtx.lineWidth = 16;
      cacheCtx.globalAlpha = 1;
      cacheCtx.setLineDash([54, 46]);
      cacheCtx.beginPath();
      cacheCtx.moveTo((this.mark_x - 9) / this.sideLength, (this.mark_y - 9) / this.sideLength);
      cacheCtx.lineTo(
        (this.mark_x + this.mark_width + 9) / this.sideLength,
        (this.mark_y - 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.mark_x + this.mark_width + 9) / this.sideLength,
        (this.mark_y + this.mark_height + 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.mark_x - 9) / this.sideLength,
        (this.mark_y + this.mark_height + 9) / this.sideLength,
      );
      cacheCtx.closePath();
      cacheCtx.stroke();
      cacheCtx.restore();

      cacheCtx.save();
      cacheCtx.globalCompositeOperation = 'source-over';
      cacheCtx.resetTransform();
      cacheCtx.strokeStyle = '#ff0000';
      cacheCtx.lineWidth = 8;
      cacheCtx.globalAlpha = 1;
      cacheCtx.setLineDash([50, 50]);
      cacheCtx.beginPath();
      cacheCtx.moveTo((this.mark_x - 9) / this.sideLength, (this.mark_y - 9) / this.sideLength);
      cacheCtx.lineTo(
        (this.mark_x + this.mark_width + 9) / this.sideLength,
        (this.mark_y - 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.mark_x + this.mark_width + 9) / this.sideLength,
        (this.mark_y + this.mark_height + 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.mark_x - 9) / this.sideLength,
        (this.mark_y + this.mark_height + 9) / this.sideLength,
      );
      cacheCtx.closePath();
      cacheCtx.stroke();
      cacheCtx.restore();

      cacheCtx.save();
      cacheCtx.globalCompositeOperation = 'source-over';
      cacheCtx.resetTransform();
      cacheCtx.strokeStyle = '#cccccc';
      cacheCtx.lineWidth = 16;
      cacheCtx.globalAlpha = 1;
      cacheCtx.setLineDash([54, 46]);
      cacheCtx.beginPath();
      cacheCtx.moveTo(
        (this.clip_mark_x + 9) / this.sideLength,
        (this.clip_mark_y + 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.clip_mark_x + this.clip_mark_width - 9) / this.sideLength,
        (this.clip_mark_y + 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.clip_mark_x + this.clip_mark_width - 9) / this.sideLength,
        (this.clip_mark_y + this.clip_mark_height - 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.clip_mark_x + 9) / this.sideLength,
        (this.clip_mark_y + this.clip_mark_height - 9) / this.sideLength,
      );
      cacheCtx.closePath();
      cacheCtx.stroke();
      cacheCtx.restore();

      cacheCtx.save();
      cacheCtx.globalCompositeOperation = 'source-over';
      cacheCtx.resetTransform();
      cacheCtx.strokeStyle = '#ff0000';
      cacheCtx.lineWidth = 8;
      cacheCtx.globalAlpha = 1;
      cacheCtx.setLineDash([50, 50]);
      cacheCtx.beginPath();
      cacheCtx.moveTo(
        (this.clip_mark_x + 9) / this.sideLength,
        (this.clip_mark_y + 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.clip_mark_x + this.clip_mark_width - 9) / this.sideLength,
        (this.clip_mark_y + 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.clip_mark_x + this.clip_mark_width - 9) / this.sideLength,
        (this.clip_mark_y + this.clip_mark_height - 9) / this.sideLength,
      );
      cacheCtx.lineTo(
        (this.clip_mark_x + 9) / this.sideLength,
        (this.clip_mark_y + this.clip_mark_height - 9) / this.sideLength,
      );
      cacheCtx.closePath();
      cacheCtx.stroke();
      cacheCtx.restore();

      ctx.save();
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.globalAlpha = 1;
      ctx.drawImage(this.cacheCanvas, 0, 0, 1024, 1024);
      ctx.restore();
    }
  }
}
